﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Algorithms;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Threading;
using System.Text;
using System.Linq.Expressions;
using Algorithms.SetResolver;
using Algorithms.Expressions;


namespace VisualizedTester.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public const string InputSetPropertyName = "InputSet";
        public const string GenerationSizePropertyName = "GenerationSize";
        public const string CrossoverRatePropertyName = "CrossoverRate";
        public const string MutationRatePropertyName = "MutationRate";
        public const string GeneLengthPropertyName = "GeneLength";
        public const string SupportRelativeFuntionsPropertyName = "SupportRelativeFunctions";
        public const string ReduceFunctionsPropertyName = "ReduceFunctions";

        public const string GenerationCountPropertyName = "GenerationCount";
        public const string BestRankingPropertyName = "BestRanking";
        public const string AverageRankingPropertyName = "AverageRanking";

        public const string OutputSetPropertyName = "OutputSet";
        public const string ResultFunctionPropertyName = "ResultFunction";
        public const string TestInputPropertyName = "TestInput";
        public const string TestOutputPropertyName = "TestOutput";
        

        private string _inputSet = "1:4, 8, 12, 16";
        private int _generationSize = 2000;
        private int _geneLength = 7;
        private double _crossoverRate = 0.80d;
        private double _mutationRate = 0.05d;
        private bool _supportRelativeFunctions;
        private bool _reduceFunctions = true;

        private int _generationCount;
        private string _bestRanking;
        private string _averageRanking;

        private string _outputSet;
        private string _resultFunction;
        private double _testInput;
        private double _testOutput;

        private Algorithms.GeneticAlgorithm _algorithm;
        private Timer _timer;
        private Expression _resultExpressionTree;

        public string InputSet
        {
            get { return _inputSet; }
            set
            {
                if (_inputSet != value)
                {
                    _inputSet = value;

                    RaisePropertyChanged(InputSetPropertyName);
                }
            }
        }

        public int GenerationSize
        {
            get { return _generationSize; }
            set
            {
                if (_generationSize != value)
                {
                    _generationSize = value;

                    RaisePropertyChanged(GenerationSizePropertyName);
                }
            }
        }

        public int GeneLength
        {
            get { return _geneLength; }
            set
            {
                if (_geneLength != value)
                {
                    _geneLength = value;

                    RaisePropertyChanged(GeneLengthPropertyName);
                }
            }
        }

        public double CrossoverRate
        {
            get { return _crossoverRate; }
            set
            {
                if (_crossoverRate != value)
                {
                    _crossoverRate = value;

                    RaisePropertyChanged(CrossoverRatePropertyName);
                }
            }
        }


        public double MutationRate
        {
            get { return _mutationRate; }
            set
            {
                if (_mutationRate != value)
                {
                    _mutationRate = value;

                    RaisePropertyChanged(MutationRatePropertyName);
                }
            }
        }

        public int GenerationCount
        {
            get { return _generationCount; }
            set
            {
                if (_generationCount != value)
                {
                    _generationCount = value;

                    RaisePropertyChanged(GenerationCountPropertyName);
                }
            }
        }

        public bool ReduceFunctions
        {
            get { return _reduceFunctions; }
            set
            {
                if (_reduceFunctions != value)
                {
                    _reduceFunctions = value;

                    RaisePropertyChanged(ReduceFunctionsPropertyName);
                }
            }
        }

        public string BestRanking
        {
            get { return _bestRanking; }
            set
            {
                if (_bestRanking != value)
                {
                    _bestRanking = value;

                    RaisePropertyChanged(BestRankingPropertyName);
                }
            }
        }


        public string AverageRanking
        {
            get { return _averageRanking; }
            set
            {
                if (_averageRanking != value)
                {
                    _averageRanking = value;

                    RaisePropertyChanged(AverageRankingPropertyName);
                }
            }
        }

        public string OutputSet
        {
            get { return _outputSet; }
            set
            {
                if (_outputSet != value)
                {
                    _outputSet = value;

                    RaisePropertyChanged(OutputSetPropertyName);
                }
            }
        }

        public string ResultFunction
        {
            get { return _resultFunction; }
            set
            {
                if (_resultFunction != value)
                {
                    _resultFunction = value;

                    RaisePropertyChanged(ResultFunctionPropertyName);
                }
            }
        }

        public double TestInput
        {
            get { return _testInput; }
            set
            {
                if (_testInput != value)
                {
                    _testInput = value;

                    RaisePropertyChanged(TestInputPropertyName);
                }
            }
        }

        public double TestOutput
        {
            get { return _testOutput; }
            set
            {
                if (_testOutput != value)
                {
                    _testOutput = value;

                    RaisePropertyChanged(TestOutputPropertyName);
                }
            }
        }

        public bool SupportRelativeFunctions
        {
            get { return _supportRelativeFunctions; }
            set
            {
                if (_supportRelativeFunctions != value)
                {
                    _supportRelativeFunctions = value;

                    RaisePropertyChanged(SupportRelativeFuntionsPropertyName);
                }
            }
        }

        public RelayCommand StartCommand { get; set; }
        public RelayCommand StopCommand { get; set; }
        public RelayCommand TestInputCommand { get; set; }
       
        public MainViewModel()
        {
            StartCommand = new RelayCommand(() =>
            {
                InputSetEntry[] inputSet = ParseInputSet(InputSet).ToArray();

                SetResolverTarget target = new SetResolverTarget(inputSet, GeneLength)
                {
                    SupportRelativeFunctions = SupportRelativeFunctions
                };

                ISortingStrategy sortingStrategy = new QuickSortStrategy();
                _algorithm = new GeneticAlgorithm(target, sortingStrategy);

                OutputSet = null;

                ThreadPool.QueueUserWorkItem(x =>
                {
                    int[] result;

                    if (_algorithm.Work(out result))
                    {
                        var resultExpressionTree = target.BuildExpressionTree(result);

                        StringBuilder outputBuilder = new StringBuilder();
                        bool first = true;

                        for (int i = 0; i < 20; i++)
                        {
                            if (first)
                            {
                                first = false;
                            }
                            else
                            {
                                outputBuilder.Append(" ,");
                            }

                            double outputResult = new LambdaExpressionTreeEvaluator().Evaluate(resultExpressionTree, i);

                            outputBuilder.Append(outputResult);
                        }

                        OutputSet = outputBuilder.ToString();



                        if (ReduceFunctions)
                        {
                            ExpressionTreeReducer reducer = new ExpressionTreeReducer();
                            resultExpressionTree = reducer.Visit(resultExpressionTree);
                        }

                        ResultFunction = resultExpressionTree.ToString();
                        _resultExpressionTree = resultExpressionTree;

                        _algorithm = null;
                    }


                    StartCommand.RaiseCanExecuteChanged();
                });

                StopCommand.RaiseCanExecuteChanged();
                StartCommand.RaiseCanExecuteChanged();
                TestInputCommand.RaiseCanExecuteChanged();
            }, () => _algorithm == null);

            StopCommand = new RelayCommand(() =>
            {
                _algorithm.Reset();
                _algorithm = null;

                AverageRanking = null;
                BestRanking = null;

                // resetting commands is done in the start command
            }, () => _algorithm != null);

            TestInputCommand = new RelayCommand(() =>
            {
                TestOutput = new LambdaExpressionTreeEvaluator().Evaluate(_resultExpressionTree, TestInput);   
            }, () => _resultExpressionTree != null);

            _timer = new Timer(x =>
            {
                GeneticAlgorithm algorithm = _algorithm;
                if (algorithm != null)
                {
                    AverageRanking = string.Format("{0} %", Math.Round(algorithm.AverageRanking));
                    BestRanking = string.Format("{0} %", Math.Round(algorithm.BestRanking));
                    GenerationCount = algorithm.GenerationCount;
                }
            }, null, 0, 100);

        }

        private static IEnumerable<InputSetEntry> ParseInputSet(string input)
        {
            var parts = from entry in input.Split(',')
                        let indexLength = entry.IndexOf(':')
                        let customIndex = indexLength > 0 ? entry.Substring(0, indexLength) : null
                        let value = indexLength > -1 ? entry.Substring(indexLength + 1) : entry
                        select new
                        {
                            Index = customIndex == null ? (int?)null : int.Parse(customIndex),
                            Value = double.Parse(value)
                        };

            // default index will be this + 1
            int lastIndex = 0;

            foreach (var part in parts)
            {
                int index;

                if (part.Index == null)
                {
                    index = lastIndex + 1;
                }
                else
                {
                    index = part.Index.Value;
                }

                lastIndex = index;

                yield return new InputSetEntry(index, part.Value);
            }
        }
    }
}